package db

import (
	"context"
	"errors"
	"github.com/go-redis/redis/v8"
	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
	"github.com/zeromicro/go-zero/core/syncx"
	"go.uber.org/zap"
	"sieve_engine/execute/whatsapp/lib/libsignal/logger"
	"strconv"
	"strings"
	"sync"
	"time"
)

var queueName string
var zredisPool = syncx.NewPool(100, create, destroy, syncx.WithMaxAge(time.Second*5))
var gredisOptions *redis.Options
var optOnce sync.Once

func create() interface{} {
	//redisClient := zredis.New("35.198.198.102:6379", zredis.WithPass("aje25!*djDK2S"))
	//redisClient := red.New(redisAddress, red.WithPass(redisPwd))
	redisClient := redis.NewClient(redisOptions())
	return redisClient
}
func destroy(obj interface{}) {
	//client := obj.(*redis.Redis)
}

func redisOptions() *redis.Options {
	optOnce.Do(func() {
		conf := g.Cfg().GetString("redis.default")
		cf := strings.Split(conf, ",")
		redisAddr := cf[0]
		dbCode, _ := strconv.Atoi(cf[1])
		redisPwd := cf[2]

		queueName = g.Cfg().GetString("redis.topic")

		gredisOptions = &redis.Options{
			DB:           dbCode,
			Addr:         redisAddr,
			Password:     redisPwd,
			MinIdleConns: 0,
			MaxConnAge:   0,
			//IdleTimeout: 2 * time.Second,
			DialTimeout:  10 * time.Second,
			ReadTimeout:  30 * time.Second,
			WriteTimeout: 30 * time.Second,

			MaxRetries: -1,

			PoolSize:           10,
			PoolTimeout:        30 * time.Second,
			IdleTimeout:        time.Minute,
			IdleCheckFrequency: 100 * time.Millisecond,
		}
		//fmt.Println(gredisOptions)
	})
	return gredisOptions
}

/*
*
定义推送结构
*/
type PushMsg struct {
	UserName uint64
	Type     int
	Time     int64
	Data     interface{}
	Text     string
}

/***
* 发送队例
 */
func PushQueue(i interface{}) {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	if i == nil {
		return
	}
	ctx := context.Background()
	result := client.RPush(ctx, queueName, gconv.String(i))
	if result.Err() != nil {
		logger.Error(result.Err().Error())
		return
	}
	if result.Val() <= 0 {
		logger.Error("PushQueue error")
	}
}

/***
* 发送队例
 */
func PushInQueue(qn string, i interface{}) {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)

	if i == nil {
		return
	}
	ctx := context.Background()
	result := client.RPush(ctx, qn, gconv.String(i))
	if result.Err() != nil {
		logger.Error(result.Err().Error())
		return
	}
	if result.Val() <= 0 {
		logger.Error("PushInQueue error")
	}
}
func RedisKeyAddCount(key string, rowKey string) error {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)

	//client := redis.NewClient(redisOptions())
	//defer client.Close()
	dateKey := time.Now().Format("2006-01-02") //YYYY-MM-DD_rowKey
	if rowKey != "" {
		dateKey += "_" + rowKey
	}

	//eval, err := PanelClient.Eval(`return redis.call("EXISTS", KEYS[1])`, []string{"go_counter_login_error"})
	/*
		local curVal = redis.call("HGET", KEYS[1], KEYS[2]);
		if curVal == false then
			curVal = 0
		else
			curVal = tonumber(curVal)
		end
		curVal = curVal + 1
		redis.call("HSET", KEYS[1], KEYS[2], curVal)
		return curVal

		local curVal = redis.call("HGET", KEYS[1], KEYS[2]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end;curVal = curVal + 1;redis.call("HSET", KEYS[1], KEYS[2], curVal);return curVal
	*/

	script1 := `local curVal = redis.call("HGET", KEYS[1], KEYS[2]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end;curVal = curVal + 1;redis.call("HSET", KEYS[1], KEYS[2], curVal);return curVal`
	cmd := client.Eval(context.Background(), script1, []string{key, dateKey})
	if cmd.Err() != nil {
		logger.Error("RedisKeyAddCount", zap.String("error", cmd.Err().Error()))
		return cmd.Err()
	}
	if count, _ := cmd.Int(); count >= 0 {
		//logger.Info(count)
		return nil
	}
	return errors.New("redis SAdd error")
}

func SETExpirationObj(k string, i interface{}, expiration time.Duration) error {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	iData, err := gjson.Encode(i)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	ctx := context.Background()
	var statusCmd *redis.StatusCmd
	if expiration > 0 {
		statusCmd = client.SetEX(ctx, k, string(iData), expiration)
	} else {
		statusCmd = client.Set(ctx, k, string(iData), 0)
	}
	if statusCmd.Err() != nil {
		logger.Error(statusCmd.Err().Error())
		return err
	}

	return nil
}

func SETObj(k string, i interface{}) error {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	iData, err := gjson.Encode(i)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	statusCmd := client.Set(context.Background(), k, string(iData), 0)
	if statusCmd.Err() != nil {
		logger.Error(statusCmd.Err().Error())
		return err
	}

	return nil
}

func GETObj(k string, i interface{}) error {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	stringCmd := client.Get(context.Background(), k)
	if stringCmd.Err() != nil {
		return stringCmd.Err()
	}
	err := gjson.DecodeTo(stringCmd.Val(), &i)
	//err := json.Unmarshal([]byte(stringCmd.Val()), &i)
	if err != nil {
		return err
	}
	return nil
}

func DelObj(k string) error {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	intCmd := client.Del(context.Background(), k)
	if intCmd.Err() != nil {
		return intCmd.Err()
	}
	return nil
}

func Exists(k string) (bool, error) {
	client := zredisPool.Get().(*redis.Client)
	defer zredisPool.Put(client)
	//client := gredisOptions.GetWa().(*red.Redis)
	//defer gredisOptions.Put(client)

	intCmd := client.Exists(context.Background(), k)
	if intCmd.Err() != nil {
		logger.Error(intCmd.Err().Error())
		return false, intCmd.Err()
	}
	return true, nil
}
